package com.acmedcare.framework.exchange.center.executer;

import com.acmedcare.framework.exchange.center.core.CommonCommandListener;
import com.acmedcare.framework.exchange.center.core.Const;
import com.acmedcare.framework.exchange.center.core.JmxContainerUtil;
import com.acmedcare.framework.exchange.center.core.PluginContainerMBean;
import com.acmedcare.framework.exchange.center.core.handler.PluginCommandHandler;
import com.acmedcare.framework.exchange.center.core.handler.StageCommandHandler;
import com.acmedcare.framework.exchange.center.core.storage.PluginRepository;
import com.acmedcare.framework.exchange.center.entity.bean.Plugin;
import com.acmedcare.framework.exchange.center.executer.job.HealthCheckJob;
import com.acmedcare.framework.starter.bus.AcmedcareEventPublisher;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantLock;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class ApplicationReadyEventListener {

  private final ApplicationContext applicationContext;
  private ExecutorConfig config;
  private final AcmedcareEventPublisher publisher;
  private final Scheduler scheduler;
  private final PluginContainerMBean container;
  private final PluginRepository pluginRepository;
  private final PluginCommandHandler pluginCommandHandler;
  private RabbitTemplate rabbitTemplate;
  private volatile Boolean started = false;
  private final StageCommandHandler stageCommandHandler;

  private final ReentrantLock reentrantLock = new ReentrantLock();

  public ApplicationReadyEventListener(
      ApplicationContext applicationContext,
      AcmedcareEventPublisher publisher, ExecutorConfig config,
      PluginContainerMBean container,
      PluginRepository repository,
      PluginCommandHandler pluginCommandHandler,
      RabbitTemplate rabbitTemplate,
      StageCommandHandler stageCommandHandler) throws SchedulerException {
    this.applicationContext = applicationContext;
    this.config = config;
    this.publisher = publisher;
    this.container = container;
    this.pluginRepository = repository;
    this.pluginCommandHandler = pluginCommandHandler;
    this.rabbitTemplate = rabbitTemplate;
    this.stageCommandHandler = stageCommandHandler;
    scheduler = new StdSchedulerFactory().getScheduler();
  }

  public void init() {

    reentrantLock.lock();
    if (started) {

      reentrantLock.unlock();
      return;
    }

    started = true;
    reentrantLock.unlock();

    JmxContainerUtil.ServerPort = applicationContext.getEnvironment()
        .getProperty("local.server.port");
    try {
      config.setApplicationContext(applicationContext);
      config.setScheduler(scheduler);
      config.setAcmedcareEventPublisher(publisher);
      config.init();

      addStageListener();
      initContainer();
      addHealthCheck();
      addPluginListener();
      if (!scheduler.isStarted()) {

        scheduler.start();
      }
    } catch (SchedulerException e) {

      log.error("executor server started fail", e);
    } finally {
      started = true;
    }
  }

  private void addHealthCheck() throws SchedulerException {

    if (scheduler
        .checkExists(new JobKey("HealthCheck", ExecutorConfig.getInstance().getGroupName()))) {

      scheduler.deleteJob(new JobKey("HealthCheck", ExecutorConfig.getInstance().getGroupName()));
      return;
    }

    JobDetail jobDetail = JobBuilder.newJob(HealthCheckJob.class)
        .withIdentity("HealthCheck", config.getGroupName())
        .build();
    Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("HealthCheck" + "Trigger", config.getGroupName())
        .withSchedule(CronScheduleBuilder.cronSchedule("0 0/1 * * * ? ")).build();
    try {
      scheduler.scheduleJob(jobDetail, trigger);
    } catch (SchedulerException e) {
      log.error("create job fail", e);
    }
  }

  /**
   * 初始化插件容器
   */
  private void initContainer() {

    List<Plugin> plugins = pluginRepository.getPlugins();
    if (plugins != null) {

      container.init(plugins);
    }
  }

  //添加插件监听程序，监听插件数据
  private void addPluginListener() {

    CommonCommandListener
        .addFanoutListener(new MessageListenerAdapter(pluginCommandHandler, "receive"),
            Const.PluginExchangeName,
            "fanout.exchange.center.worker.plugin." + config.getGroupName() + "." + UUID
                .randomUUID()
                .toString(), rabbitTemplate);
  }

  private void addStageListener() {

    stageCommandHandler.loadConfig();
    MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(stageCommandHandler,
        "receive");
    CommonCommandListener
        .addFanoutListener(messageListenerAdapter,
            Const.StageExchangeName,
            "fanout.exchange.center.worker.stage." + config.getGroupName() + "." + UUID.randomUUID()
                .toString(), rabbitTemplate);
  }
}
